{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "collapsed": true,
    "id": "8xVCNjTd_gMs",
    "outputId": "644daaf8-dcd0-4f85-8244-f903d7975b5e"
   },
   "outputs": [],
   "source": [
    "!pip install gradio grandalf huggingface-hub langchain langchain-community langchain-core langchain-openai langgraph langgraph-checkpoint langgraph-checkpoint-postgres langgraph-checkpoint-sqlite langsmith openai psycopg pydantic pydantic_core tiktoken langchain-huggingface pymupdf4llm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vsujzwHTBtPN"
   },
   "source": [
    "# Systematic Review Automation System\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qMta9MAbUz1B"
   },
   "source": [
    "A tool for automated academic literature review and synthesis\n",
    "\n",
    "## Introduction\n",
    "This system automates the process of creating systematic reviews of academic papers through a structured workflow. It handles everything from initial paper search to final draft generation using a directed graph architecture.\n",
    "\n",
    "## Use Cases\n",
    "\n",
    "**Primary Applications**\n",
    "- Conducting systematic literature reviews\n",
    "- Analyzing research trends across papers\n",
    "- Synthesizing findings from multiple studies\n",
    "- Creating comprehensive research summaries\n",
    "\n",
    "**Key Features**\n",
    "- Automated paper search and selection\n",
    "- PDF download and analysis\n",
    "- Section-by-section writing\n",
    "- Revision and critique cycles\n",
    "\n",
    "## Process Flow\n",
    "\n",
    "1. **Research Phase**\n",
    "- Topic planning and scoping\n",
    "- Automated paper search via Semantic Scholar\n",
    "- Smart paper selection (up to 3 papers - can be changed)\n",
    "- Automatic PDF retrieval\n",
    "\n",
    "2. **Analysis Phase**\n",
    "- PDF text extraction\n",
    "- Section-by-section analysis\n",
    "- Key finding identification\n",
    "- Cross-paper comparison\n",
    "\n",
    "3. **Writing Phase**\n",
    "- Automated section generation\n",
    "- Abstract (100-word limit)\n",
    "- Methods comparison\n",
    "- Results synthesis\n",
    "- APA reference formatting\n",
    "\n",
    "4. **Review Phase**\n",
    "- Quality assessment\n",
    "- Revision suggestions\n",
    "- Additional research triggers\n",
    "- Final draft preparation\n",
    "\n",
    "The system uses OpenAI's GPT models for text processing and maintains state through a graph-based workflow, ensuring systematic and thorough review generation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7YBGM7hNRSia"
   },
   "source": [
    "![download.png]()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_b92uleGIv4s"
   },
   "source": [
    "## I. Flowchart Components Description"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gHdOJKBmUvWB"
   },
   "source": [
    "### Nodes (Process Steps)\n",
    "\n",
    "1. **Initial Stages**\n",
    "- `_start_`: Beginning point of the process\n",
    "- `process_input`: Initial data processing stage\n",
    "- `planner`: Strategy development phase\n",
    "- `researcher`: Research coordination phase\n",
    "\n",
    "2. **Article Management**\n",
    "- `search_articles`: Article search and identification\n",
    "- `article_decisions`: Evaluation and selection of articles\n",
    "- `download_articles`: Retrieval of selected articles\n",
    "- `paper_analyzer`: In-depth analysis of papers\n",
    "\n",
    "3. **Writing Components**\n",
    "- `write_abstract`: Abstract composition\n",
    "- `write_conclusion`: Conclusion development\n",
    "- `write_introduction`: Introduction creation\n",
    "- `write_methods`: Methodology documentation\n",
    "- `write_references`: Reference compilation\n",
    "- `write_results`: Results documentation\n",
    "\n",
    "4. **Final Stages**\n",
    "- `aggregate_paper`: Combining all sections\n",
    "- `critique_paper`: Critical review phase\n",
    "- `revise_paper`: Revision process\n",
    "- `final_draft`: Final document preparation\n",
    "- `_end_`: Process completion\n",
    "\n",
    "## Edges (Connections)\n",
    "\n",
    "1. **Main Flow**\n",
    "- Solid arrows indicate direct progression between steps\n",
    "- Sequential flow from start through research phases\n",
    "- Parallel paths from paper_analyzer to writing components\n",
    "\n",
    "2. **Special Connections**\n",
    "- Dotted line with \"True\" label: Feedback loop to search_articles\n",
    "- \"revise\" connection: Loop between critique_paper and revise_paper\n",
    "- Multiple converging arrows into aggregate_paper from all writing components\n",
    "\n",
    "3. **Decision Points**\n",
    "- Branching at paper_analyzer to multiple writing tasks\n",
    "- Convergence at aggregate_paper from all writing components\n",
    "- Split path at critique_paper leading to either revision or final draft"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "jclcOF4PBrRd"
   },
   "source": [
    "## II. Imports\n",
    "- if you have postgres set up you can use that\n",
    "- we will use the MemorySaver() to store memory state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "1oMv72E5Ckdb"
   },
   "outputs": [],
   "source": [
    "from google.colab import userdata\n",
    "import os\n",
    "os.environ[\"OPENAI_API_KEY\"] = userdata.get('OPENAI_API_KEY')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "fdMhxiJFByvq"
   },
   "outputs": [],
   "source": [
    "from pydantic import BaseModel, Field\n",
    "from typing import TypedDict, Annotated, List, Dict, Any, Type\n",
    "import requests\n",
    "from langchain_core.tools import BaseTool\n",
    "\n",
    "from bs4 import BeautifulSoup\n",
    "import pymupdf4llm\n",
    "import sys\n",
    "from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage, ToolMessage, AIMessage, ChatMessage\n",
    "\n",
    "import requests\n",
    "import ast\n",
    "import operator\n",
    "\n",
    "from langgraph.graph import StateGraph, END\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "import os\n",
    "from uuid import uuid4\n",
    "from langgraph.checkpoint.postgres import PostgresSaver\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from psycopg import Connection\n",
    "from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint\n",
    "\n",
    "from tenacity import (\n",
    "    retry,\n",
    "    stop_after_attempt,\n",
    "    wait_exponential,\n",
    "    retry_if_exception_type\n",
    ")\n",
    "\n",
    "import openai\n",
    "from dotenv import load_dotenv\n",
    "_ = load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zuZhH9xFB2mO"
   },
   "source": [
    "## III. Academic Search Tool"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2UkFeXa3UlE_"
   },
   "source": [
    "This tool helps researchers and students search for academic papers efficiently. It connects to the Semantic Scholar API and returns structured paper information.\n",
    "\n",
    "### Main Components\n",
    "\n",
    "**Input Parameters**\n",
    "- `topic`: Your research subject\n",
    "- `max_results`: Number of papers to retrieve (default: 20)\n",
    "\n",
    "**Output Format**\n",
    "Each paper result includes:\n",
    "- Title\n",
    "- Abstract\n",
    "- Author list\n",
    "- Publication year\n",
    "- PDF link (if openly accessible)\n",
    "\n",
    "## Key Features\n",
    "\n",
    "**Search Capabilities**\n",
    "- Connects to Semantic Scholar's database\n",
    "- Filters for open access papers\n",
    "- Returns structured, easy-to-process results\n",
    "\n",
    "## Notes\n",
    "- Only returns open access papers\n",
    "- Async operations not currently supported\n",
    "- Requires valid API connection\n",
    "- Results are paginated for efficiency\n",
    "\n",
    "This tool simplifies academic research by providing structured access to scholarly papers while handling common search and retrieval challenges automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "QD31tU5NBBcY"
   },
   "outputs": [],
   "source": [
    "class AcademicPaperSearchInput(BaseModel):\n",
    "    topic: str = Field(..., description=\"The topic to search for academic papers on\")\n",
    "    max_results: int = Field(20, description=\"Maximum number of results to return\")\n",
    "\n",
    "class AcademicPaperSearchTool(BaseTool):\n",
    "    args_schema: type = AcademicPaperSearchInput  # Explicit type annotation\n",
    "    name: str = Field(\"academic_paper_search_tool\", description=\"Tool for searching academic papers\")\n",
    "    description: str = Field(\"Queries an academic papers API to retrieve relevant articles based on a topic\")\n",
    "\n",
    "    def __init__(self, name: str = \"academic_paper_search_tool\",\n",
    "                 description: str = \"Queries an academic paper API to retrieve relevant articles based on a topic\"):\n",
    "        super().__init__()\n",
    "        self.name = name\n",
    "        self.description = description\n",
    "\n",
    "    def _run(self, topic: str, max_results: int) -> List[Dict[str, Any]]:\n",
    "        # Query an external academic API like arXiv, Semantic Scholar, or CrossRef\n",
    "        search_results = self.query_academic_api(topic, max_results)\n",
    "        # testing = search_results[0]['text'][:100]\n",
    "\n",
    "        return search_results\n",
    "\n",
    "    async def _arun(self, topic: str, max_results: int) -> List[Dict[str, Any]]:\n",
    "        raise NotImplementedError(\"Async version not implemented\")\n",
    "\n",
    "    def query_academic_api(self, topic: str, max_results: int) -> List[Dict[str, Any]]:\n",
    "        base_url = \"https://api.semanticscholar.org/graph/v1/paper/search\"\n",
    "        params = {\n",
    "            \"query\": topic,\n",
    "            \"limit\": max_results, # max_results\n",
    "            \"fields\": \"title,abstract,authors,year,openAccessPdf\",\n",
    "            \"openAccessPdf\" : True\n",
    "        }\n",
    "        try:\n",
    "            while True:\n",
    "                try:\n",
    "                    response = requests.get(base_url, params=params)\n",
    "                    print(response)\n",
    "\n",
    "                    if response.status_code == 200:\n",
    "                        papers = response.json().get(\"data\", [])\n",
    "                        formatted_results = [\n",
    "                            {\n",
    "                                \"title\"     : paper.get(\"title\"),\n",
    "                                \"abstract\"  : paper.get(\"abstract\"),\n",
    "                                \"authors\"   : [author.get(\"name\") for author in paper.get(\"authors\", [])],\n",
    "                                \"year\"      : paper.get(\"year\"),\n",
    "                                \"pdf\"       : paper.get(\"openAccessPdf\"),\n",
    "                            }\n",
    "                            for paper in papers\n",
    "                        ]\n",
    "\n",
    "                        return formatted_results\n",
    "                except:\n",
    "                    # raise ValueError(f\"Failed to fetch papers: {response.status_code} - {response.text}\")\n",
    "                    print((f\"Failed to fetch papers: {response.status_code} - {response.text}. Trying Again...\"))\n",
    "        except KeyboardInterrupt:\n",
    "            print(\"\\nOperation cancelled by user\")\n",
    "            sys.exit(0)  # Clean exit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rt0b1YgrA5kt"
   },
   "source": [
    "## IV. Prompts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dXA9zkOgUfpj"
   },
   "source": [
    "### Planning Phase Prompts\n",
    "\n",
    "**Planner Prompt**\n",
    "- Acts as initial architect of the review\n",
    "- Sets up structure based on standard academic components\n",
    "- Creates outline without conducting actual research\n",
    "- Focuses on organization and methodology planning\n",
    "\n",
    "**Research Prompt**\n",
    "- Generates 5 targeted search queries\n",
    "- Uses project plan to guide search strategy\n",
    "- Interfaces with academic paper search tool\n",
    "- Ensures comprehensive literature coverage\n",
    "\n",
    "**Decision Prompt**\n",
    "- Evaluates search results against project plan\n",
    "- Selects top 3 most relevant papers - this number can be changed as you see fit!\n",
    "- Returns only PDF URLs in JSON format\n",
    "- Streamlines paper selection process\n",
    "\n",
    "## Analysis Phase Prompts\n",
    "\n",
    "**Analyze Paper Prompt**\n",
    "- Breaks down papers into key sections\n",
    "- Provides section-specific analysis:\n",
    "  - Abstract: Key points\n",
    "  - Introduction: Research motivation\n",
    "  - Methods: Technical details and mathematical analysis\n",
    "  - Results: Statistical findings\n",
    "  - Conclusions: Analysis and counterarguments\n",
    "- Includes metadata (title, year, authors, URL)\n",
    "\n",
    "## Writing Phase Prompts\n",
    "\n",
    "**Section-Specific Prompts:**\n",
    "- Abstract (100-word limit, overview)\n",
    "- Introduction (comprehensive background)\n",
    "- Methods (comparative analysis of approaches)\n",
    "- Results (cross-paper comparison)\n",
    "- Conclusions (synthesis and future directions)\n",
    "- References (APA formatting)\n",
    "\n",
    "## Review Phase Prompts\n",
    "\n",
    "**Critique Draft Prompt**\n",
    "- Evaluates publication readiness\n",
    "- Provides specific revision recommendations\n",
    "- Assesses need for additional research\n",
    "- Makes go/no-go publication decisions\n",
    "\n",
    "**Revise Draft Prompt**\n",
    "- Implements recommended changes\n",
    "- Refines paper based on critique\n",
    "- Ensures all feedback is addressed\n",
    "- Produces final manuscript version\n",
    "\n",
    "Each prompt works sequentially to build a comprehensive systematic review, from initial planning to final publication-ready manuscript."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "id": "Q85dRQrTAffc"
   },
   "outputs": [],
   "source": [
    "planner_prompt = '''You are an academic researcher that is planning to write a systematic review of Academic and Scientific Research Papers.\n",
    "\n",
    "A systematic review article typically includes the following components:\n",
    "Title: The title should accurately reflect the topic being reviewed, and usually includes the words \"a systematic review\".\n",
    "Abstract: A structured abstract with a short paragraph for each of the following: background, methods, results, and conclusion.\n",
    "Introduction: Summarizes the topic, explains why the review was conducted, and states the review's purpose and aims.\n",
    "Methods: Describes the methods used in the review.\n",
    "Results: Presents the results of the review.\n",
    "Discussion: Discusses the results of the review.\n",
    "References: Lists the references used in the review.\n",
    "\n",
    "Other important components of a systematic review include:\n",
    "Scoping: A \"trial run\" of the review that helps shape the review's method and protocol.\n",
    "Meta-analysis: An optional component that uses statistical methods to combine and summarize the results of multiple studies.\n",
    "Data extraction: A central component where data is collected and organized for analysis.\n",
    "Assessing the risk of bias: Helps establish transparency of evidence synthesis results.\n",
    "Interpreting results: Involves considering factors such as limitations, strength of evidence, biases, and implications for future practice or research.\n",
    "Literature identification: An important component that sets the data to be analyzed.\n",
    "\n",
    "With this in mind, only create an outline plan based on the topic. Don't search anything, just set up the planning.\n",
    "'''\n",
    "\n",
    "research_prompt = '''You are an academic researcher that is searching Academic and Scientific Research Papers.\n",
    "\n",
    "You will be given a project plan. Based on the project plan, generate 5 queries that you will use to search the papers.\n",
    "\n",
    "Send the queries to the academic_paper_search_tool as a tool call.\n",
    "'''\n",
    "\n",
    "decision_prompt = '''You are an academic researcher that is searching Academic and Scientific Research Papers.\n",
    "\n",
    "You will be given a project plan and a list of articles.\n",
    "\n",
    "Based on the project plan and articles provided, you must choose a maximum of 3 to investigate that are most relevant to that plan.\n",
    "\n",
    "IMPORTANT: You must return ONLY a JSON array of the PDF URLs with no additional text or explanation. Your entire response should be in this exact format:\n",
    "\n",
    "[\n",
    "    \"url1\",\n",
    "    \"url2\",\n",
    "    \"url3\",\n",
    "    ...\n",
    "]\n",
    "\n",
    "Do not include any other text, explanations, or formatting.'''\n",
    "\n",
    "analyze_paper_prompt = '''You are an academic researcher trying to understand the details of scientific and academic research papers.\n",
    "\n",
    "You must look through the text provided and get the details from the Abstract, Introduction, Methods, Results, and Conclusions.\n",
    "If you are in an Abstract section, just give me the condensed thoughts.\n",
    "If you are in an Introduction section, give me a concise reason on why the research was done.\n",
    "If you are in a Methods section, give me low-level details of the approach. Analyze the math and tell me what it means.\n",
    "If you are in a Results section, give me low-level relevant objective statistics. Tie it in with the methods\n",
    "If you are in a Conclusions section, give me the fellow researcher's thoughts, but also come up with a counter-argument if none are given.\n",
    "\n",
    "Remember to attach the other information to the top:\n",
    "    Title : <title>\n",
    "    Year : <year>\n",
    "    Authors : <author1, author2, etc.>\n",
    "    URL : <pdf url>\n",
    "    TLDR Analysis:\n",
    "        <your analysis>\n",
    "'''\n",
    "\n",
    "########################################################\n",
    "abstract_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the Abstract section of the paper based on the systematic outline and the analyses given.\n",
    "Make the abstract no more than 100 words.\n",
    "'''\n",
    "\n",
    "introduction_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the Introduction section of the paper based on the systematic outline and the analyses given.\n",
    "Make sure it is thorough and covers information in all the papers.\n",
    "'''\n",
    "\n",
    "methods_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the Methods section of the paper based on the systematic outline and the analyses given.\n",
    "Make sure it is thorough and covers information in all the papers. Draw on the differences and similarities in approaches in each paper.\n",
    "'''\n",
    "\n",
    "results_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the Results section of the paper based on the systematic outline and the analyses given.\n",
    "Make sure it is thorough and covers information in all the papers. If there are results to compare among papers, please do so.\n",
    "'''\n",
    "\n",
    "conclusions_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the Conclusions section of the paper based on the systematic outline and the analyses given.\n",
    "Make sure it is thorough and covers information in all the papers.\n",
    "Draw on the conclusions from other papers, and what you might think the future of the research holds.\n",
    "'''\n",
    "\n",
    "references_prompt = '''You are an academic researcher that is writing a systematic review of Academic and Scientific Research Papers.\n",
    "You are tasked with writing the References section of the paper based on the systematic outline and the analyses given.\n",
    "Construct an APA style references list\n",
    "'''\n",
    "#########################################################\n",
    "critique_draft_prompt = \"\"\"You are an academic researcher deciding whether or not a systematic review should be published.\n",
    "Generate a critique and recommendations for the author's submission or generate a query to get more papers.\n",
    "\n",
    "If you think just a revision needs to be made, provide detailed recommendations, including requests for length, depth, style.\n",
    "If you think the paper is good as is, just end with the draft unchanged.\n",
    "\"\"\"\n",
    "# If you think the write-up needs more papers, generate a search query and ask only for 2 additional articles.\n",
    "\n",
    "\n",
    "revise_draft_prompt = \"\"\"You are an academic researcher that is revising a systematic review that is about to be published.\n",
    "Given the paper below, revise it following the recommendations given.\n",
    "\n",
    "Return the revised paper with the implemented recommended changes.\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KvAL9WpGKwn4"
   },
   "source": [
    "## V. Understanding Agent State"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JfAe33toUaNZ"
   },
   "source": [
    "### Core Components\n",
    "\n",
    "**Message Management**\n",
    "- `messages`: Tracks conversation history\n",
    "- `last_human_index`: Keeps track of user interaction points\n",
    "- `systematic_review_outline`: Stores the overall review structure\n",
    "\n",
    "**Paper Processing**\n",
    "- `papers`: List of downloaded papers for review\n",
    "- `analyses`: Collection of individual paper analyses\n",
    "- `combined_analysis`: Synthesized findings from all papers\n",
    "\n",
    "## Document Sections\n",
    "Each section is stored separately for flexible editing:\n",
    "- `title`: Paper's main title\n",
    "- `abstract`: Brief summary\n",
    "- `introduction`: Background and context\n",
    "- `methods`: Research methodology\n",
    "- `results`: Research findings\n",
    "- `conclusion`: Final interpretations\n",
    "- `references`: Citation list\n",
    "\n",
    "## Revision Control\n",
    "- `draft`: Current version of the paper\n",
    "- `revision_num`: Tracks revision iterations\n",
    "- `max_revisions`: Limits revision cycles\n",
    "\n",
    "## Special Features\n",
    "\n",
    "**Type Annotations**\n",
    "- `Annotated[List[str], operator.add]`: Allows list concatenation\n",
    "- `Annotated[list[AnyMessage], reduce_messages]`: Manages message history\n",
    "- `TypedDict`: Ensures type safety for all fields\n",
    "\n",
    "## Usage Notes\n",
    "- Each field maintains its own state\n",
    "- Sections can be updated independently\n",
    "- Revision tracking prevents infinite loops\n",
    "- Message history helps maintain context\n",
    "- Lists can be combined using operator.add\n",
    "\n",
    "This state management system helps track all components of a systematic review from initial research through final revision."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "WboO6Vi1LD8U"
   },
   "outputs": [],
   "source": [
    "def reduce_messages(left: list[AnyMessage], right: list[AnyMessage]) -> list[AnyMessage]:\n",
    "    # assign ids to messages that don't have them\n",
    "    for message in right:\n",
    "        if not message.id:\n",
    "            message.id = str(uuid4())\n",
    "    # merge the new messages with the existing messages\n",
    "    merged = left.copy()\n",
    "    for message in right:\n",
    "        for i, existing in enumerate(merged):\n",
    "            # replace any existing messages with the same id\n",
    "            if existing.id == message.id:\n",
    "                merged[i] = message\n",
    "                break\n",
    "        else:\n",
    "            # append any new messages to the end\n",
    "            merged.append(message)\n",
    "    return merged"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "bxtS3iDTA99l"
   },
   "outputs": [],
   "source": [
    "class AgentState(TypedDict):\n",
    "    messages: Annotated[list[AnyMessage], reduce_messages]\n",
    "    systematic_review_outline : str\n",
    "    last_human_index : int\n",
    "    papers : Annotated[List[str], operator.add] ## papers downloaded\n",
    "    analyses: Annotated[List[Dict], operator.add]  # Store analysis results\n",
    "    combined_analysis: str  # Final combined analysis\n",
    "\n",
    "    title: str\n",
    "    abstract : str\n",
    "    introduction : str\n",
    "    methods : str\n",
    "    results : str\n",
    "    conclusion : str\n",
    "    references : str\n",
    "\n",
    "    draft : str\n",
    "    revision_num : int\n",
    "    max_revisions : int"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OSzhzD2XLLjF"
   },
   "source": [
    "## VI. Creating Graph Components"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zyjVdzjsLaaa"
   },
   "source": [
    "### A. Message Processing Functions Explained"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bs1HHRc0URzI"
   },
   "source": [
    "\n",
    "#### Process Input Function\n",
    "```python\n",
    "def process_input(state: AgentState)\n",
    "```\n",
    "Sets up initial conversation state:\n",
    "- Sets revision limit to 2\n",
    "- Finds last human message in chat history\n",
    "- Returns initial settings (last_human_index, max_revisions, revision_num)\n",
    "\n",
    "## Message Filter Function\n",
    "```python\n",
    "def get_relevant_messages(state: AgentState)\n",
    "```\n",
    "Cleans up conversation history by:\n",
    "- Keeping non-empty human messages\n",
    "- Keeping completed AI responses\n",
    "- Removing system and tool messages\n",
    "- Preserving conversation flow from last human input\n",
    "\n",
    "Both functions help maintain clean conversation state and prepare messages for the systematic review process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "id": "X5yVkA_fLB4M"
   },
   "outputs": [],
   "source": [
    "def process_input(state: AgentState):\n",
    "    max_revision = 2\n",
    "    messages = state.get('messages', [])\n",
    "\n",
    "    last_human_index = len(messages) - 1\n",
    "    for i in reversed(range(len(messages))):\n",
    "        if isinstance(messages[i], HumanMessage):\n",
    "            last_human_index = i\n",
    "            break\n",
    "\n",
    "    return {\"last_human_index\": last_human_index, \"max_revisions\" : max_revision, \"revision_num\" : 1}\n",
    "\n",
    "def get_relevant_messages(state: AgentState) -> List[AnyMessage]:\n",
    "    '''\n",
    "    Don't get tool call messages for AI from history.\n",
    "    Get state from everything up to the most recent human message\n",
    "    '''\n",
    "    messages = state['messages']\n",
    "    filtered_history = []\n",
    "    for message in messages:\n",
    "        if isinstance(message, HumanMessage) and message.content!=\"\":\n",
    "            filtered_history.append(message)\n",
    "        elif isinstance(message, AIMessage) and message.content!=\"\" and message.response_metadata['finish_reason']==\"stop\":\n",
    "            filtered_history.append(message)\n",
    "    last_human_index = state['last_human_index']\n",
    "    return filtered_history[:-1] + messages[last_human_index:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GVo4-ZX3MVdU"
   },
   "source": [
    "### B. Planning and Research Node Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Tljvj35NUMmL"
   },
   "source": [
    "\n",
    "#### Plan Node\n",
    "```python\n",
    "def plan_node(state: AgentState)\n",
    "```\n",
    "Creates initial review outline:\n",
    "- Gets filtered conversation history\n",
    "- Uses planner prompt with system message\n",
    "- Generates systematic review structure\n",
    "- Returns outline in state dictionary\n",
    "\n",
    "## Research Node\n",
    "```python\n",
    "def research_node(state: AgentState)\n",
    "```\n",
    "Develops research strategy:\n",
    "- Takes review outline from state\n",
    "- Applies research prompt\n",
    "- Generates search queries\n",
    "- Updates message history\n",
    "\n",
    "**Common Elements**\n",
    "- Both use temperature parameter for response variation\n",
    "- Print progress to console\n",
    "- Return updated state components\n",
    "- Use model.invoke for AI responses\n",
    "\n",
    "These nodes represent the initial planning and research strategy phases in the systematic review flowchart, setting up the foundation for article searching and analysis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "id": "wDdVkf8UMVHw"
   },
   "outputs": [],
   "source": [
    "def plan_node(state: AgentState):\n",
    "    print(\"PLANNER\")\n",
    "    relevant_messages = get_relevant_messages(state)\n",
    "    messages = [SystemMessage(content=planner_prompt)] + relevant_messages\n",
    "    response = model.invoke(messages, temperature=temperature)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"systematic_review_outline\" : [response]}\n",
    "\n",
    "def research_node(state: AgentState):\n",
    "    print(\"RESEARCHER\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    messages = [SystemMessage(content=research_prompt)] + review_plan\n",
    "    response = model.invoke(messages, temperature=temperature)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"messages\" : [response]}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "2mm4sV0gMU_B"
   },
   "source": [
    "### C. Search Node Functions\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wbjJbTjZUHap"
   },
   "source": [
    "\n",
    "#### Take Action Node\n",
    "```python\n",
    "def take_action(state: AgentState)\n",
    "```\n",
    "Handles tool execution:\n",
    "- Gets last message from state\n",
    "- Checks for tool calls\n",
    "- Executes requested tools\n",
    "- Returns results as tool messages\n",
    "- Handles invalid tool requests\n",
    "\n",
    "## Decision Node\n",
    "```python\n",
    "def decision_node(state: AgentState)\n",
    "```\n",
    "Makes paper selection:\n",
    "- Uses review plan and message history\n",
    "- Applies decision prompt\n",
    "- Evaluates paper relevance\n",
    "- Returns selection decisions\n",
    "\n",
    "## Article Download Node\n",
    "```python\n",
    "def article_download(state: AgentState)\n",
    "```\n",
    "Manages paper downloads:\n",
    "- Takes URLs from decisions\n",
    "- Creates 'papers' directory\n",
    "- Downloads PDFs\n",
    "- Handles download errors\n",
    "- Returns file information\n",
    "\n",
    "**Common Features**\n",
    "- Error handling throughout\n",
    "- Progress logging\n",
    "- State management\n",
    "- Structured returns\n",
    "- Message formatting\n",
    "\n",
    "These nodes represent the paper selection and acquisition phase of the systematic review process, bridging planning and analysis stages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "f4SAwaFKNaBQ"
   },
   "outputs": [],
   "source": [
    "def take_action(state: AgentState):\n",
    "    ''' Get last message from agent state.\n",
    "    If we get to this state, the language model wanted to use a tool.\n",
    "    The tool calls attribute will be attached to message in the Agent State. Can be a list of tool calls.\n",
    "    Find relevant tool and invoke it, passing in the arguments\n",
    "    '''\n",
    "    print(\"GET SEARCH RESULTS\")\n",
    "    last_message = state[\"messages\"][-1]\n",
    "\n",
    "    if not hasattr(last_message, 'tool_calls') or not last_message.tool_calls:\n",
    "        return {\"messages\": state['messages']}\n",
    "\n",
    "    results = []\n",
    "    for t in last_message.tool_calls:\n",
    "        print(f'Calling: {t}')\n",
    "\n",
    "        if not t['name'] in tools: # check for bad tool name\n",
    "            print(\"\\n ....bad tool name....\")\n",
    "            result = \"bad tool name, retry\" # instruct llm to retry if bad\n",
    "        else:\n",
    "            # pass in arguments for tool call\n",
    "            result = tools[t['name']].invoke(t['args'])\n",
    "\n",
    "        # append result as a tool message\n",
    "        results.append(ToolMessage(tool_call_id = t['id'], name=t['name'], content=str(result)))\n",
    "\n",
    "    return {\"messages\" : results} # langgraph adding to state in between iterations\n",
    "\n",
    "def decision_node(state: AgentState):\n",
    "    print(\"DECISION-MAKER\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    relevant_messages = get_relevant_messages(state)\n",
    "    messages = [SystemMessage(content=decision_prompt)] + review_plan + relevant_messages\n",
    "    response = model.invoke(messages, temperature=temperature)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"messages\" : [response]}\n",
    "\n",
    "def article_download(state: AgentState):\n",
    "    print(\"DOWNLOAD PAPERS\")\n",
    "    last_message = state[\"messages\"][-1]\n",
    "\n",
    "    try:\n",
    "        # Handle different types of content\n",
    "        if isinstance(last_message.content, str):\n",
    "            urls = ast.literal_eval(last_message.content)\n",
    "        else:\n",
    "            urls = last_message.content\n",
    "\n",
    "        filenames = []\n",
    "        for url in urls:\n",
    "            try:\n",
    "                response = requests.get(url)\n",
    "                response.raise_for_status()\n",
    "\n",
    "                # Create a papers directory if it doesn't exist\n",
    "                if not os.path.exists('data'):\n",
    "                    os.makedirs('data')\n",
    "\n",
    "                # Generate a filename from the URL\n",
    "                filename = f\"data/{url.split('/')[-1]}\"\n",
    "                if not filename.endswith('.pdf'):\n",
    "                    filename += '.pdf'\n",
    "\n",
    "                # Save the PDF\n",
    "                with open(filename, 'wb') as f:\n",
    "                    f.write(response.content)\n",
    "\n",
    "                filenames.append({\"paper\" : filename})\n",
    "                print(f\"Successfully downloaded: {filename}\")\n",
    "\n",
    "            except Exception as e:\n",
    "                print(f\"Error downloading {url}: {str(e)}\")\n",
    "                continue\n",
    "\n",
    "        # Return AIMessage instead of raw strings\n",
    "        return {\n",
    "            \"papers\": [\n",
    "                AIMessage(\n",
    "                    content=filenames,\n",
    "                    response_metadata={'finish_reason': 'stop'}\n",
    "                )\n",
    "            ]\n",
    "        }\n",
    "\n",
    "    except Exception as e:\n",
    "        # Return error as AIMessage\n",
    "        return {\n",
    "            \"messages\": [\n",
    "                AIMessage(\n",
    "                    content=f\"Error processing downloads: {str(e)}\",\n",
    "                    response_metadata={'finish_reason': 'error'}\n",
    "                )\n",
    "            ]\n",
    "        }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PEa7c69xNwvT"
   },
   "source": [
    "### D. Paper Analyzer Function Explained"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "sVexDlEfUATk"
   },
   "source": [
    "\n",
    "#### Function Overview\n",
    "```python\n",
    "def paper_analyzer(state: AgentState)\n",
    "```\n",
    "\n",
    "**Purpose**: Analyzes downloaded academic papers and extracts key information.\n",
    "\n",
    "## Key Operations\n",
    "\n",
    "1. **Paper Processing**\n",
    "- Iterates through downloaded papers\n",
    "- Converts PDFs to markdown using pymupdf4llm\n",
    "- Processes each paper individually\n",
    "\n",
    "2. **Analysis Setup**\n",
    "- Creates system message with analysis prompt\n",
    "- Adds paper content as human message\n",
    "- Uses GPT-4 model for analysis\n",
    "- Sets low temperature (0.1) for consistent results\n",
    "\n",
    "3. **Output Handling**\n",
    "- Accumulates analyses for all papers\n",
    "- Returns combined analysis in state format\n",
    "- Maintains analysis history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "id": "8xLk5Z5mNxdR"
   },
   "outputs": [],
   "source": [
    "def paper_analyzer(state: AgentState):\n",
    "    print(\"ANALYZE PAPERS\")\n",
    "    analyses=\"\"\n",
    "    for paper in state['papers'][-1].content:\n",
    "        md_text = pymupdf4llm.to_markdown(f\"./{paper['paper']}\")\n",
    "        messages = [\n",
    "            SystemMessage(content=analyze_paper_prompt),\n",
    "            HumanMessage(content=md_text)\n",
    "        ]\n",
    "\n",
    "        model = ChatOpenAI(model='gpt-4o')\n",
    "        response = model.invoke(messages, temperature=0.1)\n",
    "        print(response)\n",
    "        analyses+=response.content\n",
    "    return {\n",
    "        \"analyses\": [analyses]\n",
    "    }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "U0h7B4l1OIE7"
   },
   "source": [
    "### E. Paper Writing Functions Explained"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Eq80VX2JT8Ar"
   },
   "source": [
    "\n",
    "#### API Call Handler\n",
    "```python\n",
    "def _make_api_call(model, messages, temperature=0.1)\n",
    "```\n",
    "- Manages API calls with retry logic\n",
    "- Handles rate limiting\n",
    "- Uses exponential backoff\n",
    "- Maximum 5 retry attempts\n",
    "\n",
    "## Section Writing Functions\n",
    "All section writers follow similar pattern:\n",
    "\n",
    "**Common Structure**\n",
    "- Takes state with review plan and analyses\n",
    "- Uses section-specific prompt\n",
    "- Uses GPT-4 mini model\n",
    "- Returns section content\n",
    "- Handles API calls safely\n",
    "\n",
    "**Individual Functions**\n",
    "1. `write_abstract`\n",
    "   - Creates concise summary\n",
    "   - Uses abstract prompt\n",
    "\n",
    "2. `write_introduction`\n",
    "   - Sets research context\n",
    "   - Uses introduction prompt\n",
    "\n",
    "3. `write_methods`\n",
    "   - Details methodology\n",
    "   - Uses methods prompt\n",
    "\n",
    "4. `write_results`\n",
    "   - Presents findings\n",
    "   - Uses results prompt\n",
    "\n",
    "5. `write_conclusion`\n",
    "   - Summarizes implications\n",
    "   - Uses conclusions prompt\n",
    "\n",
    "6. `write_references`\n",
    "   - Formats citations\n",
    "   - Uses references prompt\n",
    "\n",
    "Each function:\n",
    "- Prints progress\n",
    "- Uses temperature 0.1\n",
    "- Returns section in state format\n",
    "- Handles API communication safely"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "id": "igdKSHaFOBXE"
   },
   "outputs": [],
   "source": [
    "def _make_api_call(model, messages, temperature=0.1):\n",
    "    @retry(\n",
    "        retry=retry_if_exception_type(openai.RateLimitError),\n",
    "        wait=wait_exponential(multiplier=1, min=4, max=60),\n",
    "        stop=stop_after_attempt(5)\n",
    "    )\n",
    "    def _call():\n",
    "        try:\n",
    "            return model.invoke(messages, temperature=temperature)\n",
    "        except openai.RateLimitError as e:\n",
    "            print(f\"Rate limit reached. Waiting before retry... ({e})\")\n",
    "            raise\n",
    "    return _call()\n",
    "\n",
    "\n",
    "def write_abstract(state: AgentState):\n",
    "    print(\"WRITE ABSTRACT\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=abstract_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"abstract\" : [response]}\n",
    "\n",
    "def write_introduction(state: AgentState):\n",
    "    print(\"WRITE INTRODUCTION\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=introduction_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"introduction\" : [response]}\n",
    "\n",
    "def write_methods(state: AgentState):\n",
    "    print(\"WRITE METHODS\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=methods_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"methods\" : [response]}\n",
    "\n",
    "def write_results(state: AgentState):\n",
    "    print(\"WRITE RESULTS\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=results_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"results\" : [response]}\n",
    "\n",
    "def write_conclusion(state: AgentState):\n",
    "    print(\"WRITE CONCLUSION\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=conclusions_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"conclusion\" : [response]}\n",
    "\n",
    "def write_references(state: AgentState):\n",
    "    print(\"WRITE REFERENCES\")\n",
    "    review_plan = state['systematic_review_outline']\n",
    "    analyses = state['analyses']\n",
    "    messages = [SystemMessage(content=references_prompt)] + review_plan + analyses\n",
    "    model = ChatOpenAI(model='gpt-4o-mini')\n",
    "    response = _make_api_call(model, messages)\n",
    "    print(response)\n",
    "    print()\n",
    "    return {\"references\" : [response]}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kpMd0PaqOaKw"
   },
   "source": [
    "### F. Final Stage Functions\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8ze_3f5-T2mx"
   },
   "source": [
    "\n",
    "#### Aggregator\n",
    "```python\n",
    "def aggregator(state: AgentState)\n",
    "```\n",
    "Combines all paper sections:\n",
    "- Takes latest version of each section\n",
    "- Maintains proper section order\n",
    "- Adds spacing between sections\n",
    "- Returns complete draft\n",
    "\n",
    "## Critique Function\n",
    "```python\n",
    "def critique(state: AgentState)\n",
    "```\n",
    "Reviews complete draft:\n",
    "- Uses review plan as reference\n",
    "- Generates critique\n",
    "- Increments revision counter\n",
    "- Returns critique and revision number\n",
    "\n",
    "## Paper Reviser\n",
    "```python\n",
    "def paper_reviser(state: AgentState)\n",
    "```\n",
    "Implements critique feedback:\n",
    "- Takes latest critique and draft\n",
    "- Uses revision prompt\n",
    "- Generates revised version\n",
    "- Returns updated draft\n",
    "\n",
    "## Decision Function\n",
    "```python\n",
    "def exists_action(state: AgentState)\n",
    "```\n",
    "Controls workflow direction:\n",
    "- Checks revision count limit\n",
    "- Evaluates need for more research\n",
    "- Returns decision:\n",
    "  - \"final_draft\": If max revisions reached\n",
    "  - True: If more research needed\n",
    "  - \"revise\": For continued revision\n",
    "\n",
    "## Final Draft Handler\n",
    "```python\n",
    "def final_draft(state: AgentState)\n",
    "```\n",
    "Completes review process:\n",
    "- Returns final version of draft\n",
    "- Marks end of revision cycle\n",
    "\n",
    "These functions represent the final stages of the systematic review process, handling paper compilation, revision, and completion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "id": "SKIj8_7OOZpw"
   },
   "outputs": [],
   "source": [
    "def aggregator(state: AgentState):\n",
    "    print(\"AGGREGATE\")\n",
    "    abstract = state['abstract'][-1].content\n",
    "    introduction = state['introduction'][-1].content\n",
    "    methods = state['methods'][-1].content\n",
    "    results = state['results'][-1].content\n",
    "    conclusion = state['conclusion'][-1].content\n",
    "    references = state['references'][-1].content\n",
    "\n",
    "    messages = [\n",
    "            SystemMessage(content=\"Make a title for this systematic review based on the abstract. Write it in markdown.\"),\n",
    "            HumanMessage(content=abstract)\n",
    "        ]\n",
    "    title = model.invoke(messages, temperature=0.1).content\n",
    "\n",
    "    draft = title + \"\\n\\n\" + abstract + \"\\n\\n\" + introduction + \"\\n\\n\" + methods + \"\\n\\n\" + results + \"\\n\\n\" + conclusion + \"\\n\\n\" + references\n",
    "\n",
    "    return {\"draft\" : [draft]}\n",
    "\n",
    "def critique(state:AgentState):\n",
    "    print(\"CRITIQUE\")\n",
    "    draft = state[\"draft\"]\n",
    "    review_plan = state['systematic_review_outline']\n",
    "\n",
    "    messages = [SystemMessage(content=critique_draft_prompt)] + review_plan + draft\n",
    "    response = model.invoke(messages, temperature=temperature)\n",
    "    print(response)\n",
    "\n",
    "    # every critique is a call for revision\n",
    "    return {'messages' : [response], \"revision_num\": state.get(\"revision_num\", 1) + 1}\n",
    "\n",
    "def paper_reviser(state: AgentState):\n",
    "    print(\"REVISE PAPER\")\n",
    "    critique = state[\"messages\"][-1].content\n",
    "    draft = state[\"draft\"]\n",
    "\n",
    "    messages = [SystemMessage(content=revise_draft_prompt)] + [critique] + draft\n",
    "    response = model.invoke(messages, temperature=temperature)\n",
    "    print(response)\n",
    "\n",
    "    return {'draft' : [response]}\n",
    "\n",
    "def exists_action(state: AgentState):\n",
    "    '''\n",
    "    Determines whether to continue revising, end, or search for more articles\n",
    "    based on the critique and revision count\n",
    "    '''\n",
    "    print(\"DECIDING WHETHER TO REVISE, END, or SEARCH AGAIN\")\n",
    "\n",
    "    if state[\"revision_num\"] > state[\"max_revisions\"]:\n",
    "        return \"final_draft\"\n",
    "\n",
    "    # # Get the latest critique\n",
    "    critique = state['messages'][-1]\n",
    "    print(critique)\n",
    "\n",
    "    # Check if the critique response has any tool calls\n",
    "    if hasattr(critique, 'tool_calls') and critique.tool_calls:\n",
    "        # The critique suggests we need more research\n",
    "        return True\n",
    "    else:\n",
    "        # No more research needed, proceed with revision\n",
    "        return \"revise\"\n",
    "\n",
    "def final_draft(state: AgentState):\n",
    "    print(\"FINAL DRAFT\")\n",
    "    return {\"draft\" : state['draft']}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ywWFbXpGPK4d"
   },
   "source": [
    "## VII. Create Graph\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GxhCDSmuTnSl"
   },
   "source": [
    "\n",
    "### Graph Initialization\n",
    "```python\n",
    "graph = StateGraph(AgentState)\n",
    "```\n",
    "Creates a directed graph to manage the systematic review workflow using AgentState for data management.\n",
    "\n",
    "## Node Addition\n",
    "The graph adds nodes in logical groups:\n",
    "\n",
    "**Initial Processing**\n",
    "- process_input: Entry point\n",
    "- planner: Creates review strategy\n",
    "- researcher: Develops search approach\n",
    "- search_articles: Finds papers\n",
    "- article_decisions: Selects papers\n",
    "- download_articles: Gets PDFs\n",
    "- paper_analyzer: Analyzes content\n",
    "\n",
    "**Writing Sections**\n",
    "- write_abstract\n",
    "- write_introduction\n",
    "- write_methods\n",
    "- write_results\n",
    "- write_conclusion\n",
    "- write_references\n",
    "\n",
    "**Final Processing**\n",
    "- aggregate_paper: Combines sections\n",
    "- critique_paper: Reviews draft\n",
    "- revise_paper: Makes changes\n",
    "- final_draft: Completes review\n",
    "\n",
    "## Edge Connections\n",
    "\n",
    "**Main Flow**\n",
    "- Linear flow from input through paper analysis\n",
    "- Parallel paths from analyzer to writing sections\n",
    "- All writing sections converge at aggregator\n",
    "\n",
    "**Review Cycle**\n",
    "Conditional branching after critique:\n",
    "- To final_draft: If complete\n",
    "- To revise_paper: If needs changes\n",
    "- To search_articles: If needs more research\n",
    "\n",
    "The graph creates a complete workflow for systematic review generation, with built-in revision cycles and quality control."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "It9pnQy0O-TD",
    "outputId": "57a19227-9895-4fb5-c3fc-165eab0a80f9"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x7d5a7cdcfcd0>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph = StateGraph(AgentState)\n",
    "graph.add_node(\"process_input\", process_input)\n",
    "graph.add_node(\"planner\", plan_node)\n",
    "graph.add_node(\"researcher\", research_node)\n",
    "graph.add_node(\"search_articles\", take_action)\n",
    "graph.add_node(\"article_decisions\", decision_node)\n",
    "graph.add_node(\"download_articles\", article_download)\n",
    "graph.add_node(\"paper_analyzer\", paper_analyzer)\n",
    "\n",
    "graph.add_node(\"write_abstract\", write_abstract)\n",
    "graph.add_node(\"write_introduction\", write_introduction)\n",
    "graph.add_node(\"write_methods\", write_methods)\n",
    "graph.add_node(\"write_results\", write_results)\n",
    "graph.add_node(\"write_conclusion\", write_conclusion)\n",
    "graph.add_node(\"write_references\", write_references)\n",
    "\n",
    "graph.add_node(\"aggregate_paper\", aggregator)\n",
    "graph.add_node(\"critique_paper\", critique)\n",
    "graph.add_node(\"revise_paper\", paper_reviser)\n",
    "graph.add_node(\"final_draft\", final_draft)\n",
    "\n",
    "####################################\n",
    "graph.add_edge(\"process_input\", \"planner\")\n",
    "graph.add_edge(\"planner\", \"researcher\")\n",
    "graph.add_edge(\"researcher\", \"search_articles\")\n",
    "graph.add_edge(\"search_articles\", \"article_decisions\")\n",
    "graph.add_edge(\"article_decisions\", \"download_articles\")\n",
    "graph.add_edge(\"download_articles\", 'paper_analyzer')\n",
    "\n",
    "graph.add_edge(\"paper_analyzer\", \"write_abstract\")\n",
    "graph.add_edge(\"paper_analyzer\", \"write_introduction\")\n",
    "graph.add_edge(\"paper_analyzer\", \"write_methods\")\n",
    "graph.add_edge(\"paper_analyzer\", \"write_results\")\n",
    "graph.add_edge(\"paper_analyzer\", \"write_conclusion\")\n",
    "graph.add_edge(\"paper_analyzer\", \"write_references\")\n",
    "\n",
    "graph.add_edge(\"write_abstract\", \"aggregate_paper\")\n",
    "graph.add_edge(\"write_introduction\", \"aggregate_paper\")\n",
    "graph.add_edge(\"write_methods\", \"aggregate_paper\")\n",
    "graph.add_edge(\"write_results\", \"aggregate_paper\")\n",
    "graph.add_edge(\"write_conclusion\", \"aggregate_paper\")\n",
    "graph.add_edge(\"write_references\", \"aggregate_paper\")\n",
    "\n",
    "graph.add_edge(\"aggregate_paper\", 'critique_paper')\n",
    "\n",
    "graph.add_conditional_edges(\n",
    "    \"critique_paper\",\n",
    "    exists_action,\n",
    "    {\"final_draft\": \"final_draft\",\n",
    "     \"revise\": \"revise_paper\",\n",
    "     True: \"search_articles\"}\n",
    ")\n",
    "\n",
    "graph.add_edge(\"revise_paper\", \"critique_paper\")\n",
    "graph.add_edge(\"final_draft\", END)\n",
    "\n",
    "graph.set_entry_point(\"process_input\") ## \"llm\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fwKaERxoPom1"
   },
   "source": [
    "## VIII. Compile and Run Graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "id": "1miL92WyPT5k"
   },
   "outputs": [],
   "source": [
    "checkpointer = MemorySaver()\n",
    "graph = graph.compile(checkpointer=checkpointer)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "id": "MD9gFjXfPjst"
   },
   "outputs": [],
   "source": [
    "topic= \"diffusion models for music generation\"\n",
    "thread_id = \"test18\"\n",
    "temperature=0.1\n",
    "papers_tool = AcademicPaperSearchTool()\n",
    "tooling = [papers_tool]\n",
    "model=ChatOpenAI(model='gpt-4o-mini') # gpt-4o-mini\n",
    "tools = {t.name: t for t in tooling} if tooling else {}\n",
    "model = model.bind_tools(tooling) if tools else model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "C8cuZ7SJPyRF",
    "outputId": "585d0ed8-fd7d-4e9c-b8fe-adeca2bd9f6c"
   },
   "outputs": [],
   "source": [
    "agent_input = {\"messages\" : [HumanMessage(content=topic)]}\n",
    "thread_config = {\"configurable\" : {\"thread_id\" : thread_id}}\n",
    "result = graph.invoke(agent_input, thread_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000
    },
    "id": "jWBn-7e7Rc2j",
    "outputId": "ca9ff980-38c0-4498-80f5-e0aa1e9c8f97"
   },
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "# Advancements in Text-Conditioned Music Generation: A Systematic Review of Diffusion Models\n",
       "\n",
       "**Abstract**  \n",
       "This systematic review evaluates recent advancements in text-conditioned music generation using diffusion models, focusing on three notable approaches: Noise2Music, JEN-1, and ERNIE-Music. Each model employs unique methodologies to convert text prompts into high-fidelity music, demonstrating significant improvements in text-music alignment and audio quality. Noise2Music utilizes a two-stage diffusion process, JEN-1 combines autoregressive and non-autoregressive training, while ERNIE-Music directly generates waveforms from free-form text. The findings highlight the potential of diffusion models in music generation, alongside challenges such as data limitations and model interpretability, paving the way for future research in this innovative field. The implications of these findings suggest a transformative impact on the music industry and AI creativity.\n",
       "\n",
       "### Introduction\n",
       "\n",
       "The intersection of artificial intelligence and music generation has garnered significant attention in recent years, particularly with the advent of advanced machine learning techniques. Among these, diffusion models have emerged as a promising approach for generating high-quality audio content, including music, from textual descriptions. This systematic review focuses on three notable contributions to the field: Noise2Music, JEN-1, and ERNIE-Music, each of which employs diffusion models to tackle the challenge of text-conditioned music generation.\n",
       "\n",
       "Music generation from text prompts presents unique challenges due to the complexity of musical structures and the need for high fidelity in audio output. Traditional methods often rely on intermediate representations, such as spectrograms, which can lead to fidelity loss during the conversion process. The studies reviewed here explore innovative methodologies that directly generate audio waveforms, thereby enhancing the quality and alignment of the generated music with the provided text prompts.\n",
       "\n",
       "**Noise2Music** introduces a two-stage diffusion model framework that generates 30-second music clips from text prompts. The first stage involves a generator model that creates an intermediate representation conditioned on the text, while the second stage employs a cascader model to produce high-fidelity audio. This approach allows for the exploration of different intermediate representations, including log-mel spectrograms and lower-fidelity audio, ultimately demonstrating strong alignment with the text prompts in terms of genre, tempo, and mood. The reliance on pretrained language models for generating text-audio pairs further underscores the importance of leveraging existing resources to enhance model performance.\n",
       "\n",
       "**JEN-1** takes a different approach by combining autoregressive and non-autoregressive training within a diffusion framework. This model directly generates high-fidelity waveforms at a sampling rate of 48kHz, thereby avoiding the pitfalls associated with spectrogram conversion. JEN-1's architecture supports multi-task training, enabling it to perform various music generation tasks, including inpainting and continuation. The results indicate that JEN-1 excels in both text-music alignment and overall music quality, outperforming existing state-of-the-art methods while maintaining computational efficiency.\n",
       "\n",
       "**ERNIE-Music** addresses the challenge of limited text-music parallel data by creating a dataset sourced from web resources and employing weak supervision techniques. This model also focuses on generating music waveforms directly from free-form text, demonstrating that unrestricted textual prompts yield better text-music relevance compared to predefined music tags. The architecture utilizes a conditional diffusion model that incorporates text as a guiding variable, showcasing the potential for generating diverse and high-quality music.\n",
       "\n",
       "The collective findings from these studies highlight the advancements in text-conditioned music generation using diffusion models, emphasizing the importance of model architecture, training methodologies, and data sourcing. While each model presents unique strengths, they also share common challenges, such as the need for large datasets and the potential biases introduced by web-sourced data. This review aims to synthesize the methodologies, results, and implications of these studies, providing insights into the current state of research in text-to-music generation and identifying avenues for future exploration.\n",
       "\n",
       "In conclusion, the integration of diffusion models into music generation represents a significant leap forward in the field, offering new possibilities for creativity and expression. As researchers continue to refine these models and explore their applications, the potential for generating high-quality music from text prompts will likely expand, paving the way for innovative tools and experiences in music composition and production.\n",
       "\n",
       "### Methods\n",
       "\n",
       "This systematic review synthesizes findings from three recent studies that explore the application of diffusion models for text-conditioned music generation: **Noise2Music**, **JEN-1**, and **ERNIE-Music**. The review follows a structured approach to evaluate the methodologies, results, and implications of each study, focusing on their unique contributions and commonalities in the field of generative music models.\n",
       "\n",
       "#### Literature Identification\n",
       "\n",
       "A comprehensive literature search was conducted using academic databases such as Google Scholar, IEEE Xplore, and arXiv. The search terms included \"text-conditioned music generation,\" \"diffusion models,\" \"music generation,\" and \"deep learning.\" The search was limited to papers published in 2023 to ensure the inclusion of the most recent advancements in the field. The URLs provided for each study were also utilized to access the full texts for detailed analysis.\n",
       "\n",
       "#### Inclusion and Exclusion Criteria\n",
       "\n",
       "Inclusion criteria for this review were as follows:\n",
       "- Studies published in 2023 that focus on text-to-music generation using diffusion models.\n",
       "- Research that provides empirical results and evaluations of the proposed models.\n",
       "- Papers that explore different methodologies, architectures, or datasets relevant to the topic.\n",
       "\n",
       "Exclusion criteria included:\n",
       "- Studies that do not specifically address text-conditioned music generation.\n",
       "- Papers that focus solely on theoretical frameworks without empirical validation.\n",
       "- Research published prior to 2023.\n",
       "\n",
       "#### Data Extraction\n",
       "\n",
       "Data extraction involved a systematic approach to gather relevant information from each study. Key aspects extracted included:\n",
       "- **Model Architecture**: Details on the diffusion model architecture, including the types of representations used (e.g., spectrograms, waveforms).\n",
       "- **Training Methodology**: Information on the training datasets, conditioning methods, and any unique training techniques employed (e.g., multi-task training, weak supervision).\n",
       "- **Evaluation Metrics**: Metrics used to assess model performance, such as Frechet Audio Distance (FAD), MuLan similarity scores, and qualitative assessments.\n",
       "- **Key Findings**: Summarized results regarding the effectiveness of the models in generating music that aligns with text prompts.\n",
       "\n",
       "#### Assessing the Risk of Bias\n",
       "\n",
       "The quality of the studies was assessed using a standardized checklist that evaluated the following criteria:\n",
       "- Clarity of research objectives and hypotheses.\n",
       "- Appropriateness of the methodology and model selection.\n",
       "- Transparency in reporting results and evaluation metrics.\n",
       "- Consideration of limitations and potential biases in the data sources.\n",
       "\n",
       "Each study was critically analyzed for potential biases, such as reliance on large datasets or pretrained models, which may affect the generalizability of the findings.\n",
       "\n",
       "#### Comparative Analysis\n",
       "\n",
       "A comparative analysis was conducted to highlight the similarities and differences in approaches among the three studies:\n",
       "\n",
       "1. **Model Architecture**:\n",
       "   - **Noise2Music** employs a two-stage process with a generator and a cascader model, utilizing both spectrograms and waveforms as intermediate representations.\n",
       "   - **JEN-1** directly generates waveforms, integrating autoregressive and non-autoregressive training to enhance efficiency and fidelity.\n",
       "   - **ERNIE-Music** also focuses on waveform generation but emphasizes the use of free-form text conditioning to improve text-music relevance.\n",
       "\n",
       "2. **Training Methodology**:\n",
       "   - **Noise2Music** utilizes a large dataset of music-text pairs generated by pretrained language models, emphasizing the importance of text embeddings.\n",
       "   - **JEN-1** incorporates multi-task training, allowing for various music generation tasks, including inpainting and continuation.\n",
       "   - **ERNIE-Music** addresses the challenge of limited text-music parallel data by creating a dataset from web resources and employing weak supervision techniques.\n",
       "\n",
       "3. **Evaluation Metrics**:\n",
       "   - All three studies utilize FAD and qualitative assessments to evaluate music quality and alignment with text prompts. However, **JEN-1** and **ERNIE-Music** report additional metrics, such as CLAP scores, to provide a more comprehensive evaluation of performance.\n",
       "\n",
       "4. **Key Findings**:\n",
       "   - All studies demonstrate significant advancements in text-to-music generation, with **JEN-1** and **ERNIE-Music** showing superior performance in text-music alignment and quality compared to existing methods. **Noise2Music** highlights the trade-offs between scalability and interpretability in model design.\n",
       "\n",
       "#### Limitations and Future Directions\n",
       "\n",
       "The review acknowledges limitations in the studies, such as the reliance on large datasets and pretrained models, which may restrict applicability to less-resourced languages or music styles. Future research directions include enhancing model interpretability, exploring external knowledge integration for improved controllability, and expanding datasets to include a wider variety of music genres and styles.\n",
       "\n",
       "This systematic review provides a comprehensive overview of the current state of text-conditioned music generation using diffusion models, highlighting the innovative approaches and findings from the selected studies.\n",
       "\n",
       "### Results\n",
       "\n",
       "The systematic review of the three recent studies—Noise2Music, JEN-1, and ERNIE-Music—reveals significant advancements in text-conditioned music generation using diffusion models. Each study presents unique methodologies, datasets, and evaluation metrics, contributing to the growing body of knowledge in this field.\n",
       "\n",
       "#### Study Characteristics\n",
       "\n",
       "1. **Noise2Music**:\n",
       "   - **Authors**: Huang et al. (2023)\n",
       "   - **Methodology**: Utilizes a two-stage diffusion model comprising a generator and a cascader. The generator creates an intermediate representation (either a log-mel spectrogram or a lower-fidelity waveform) from text prompts, while the cascader produces high-fidelity audio.\n",
       "   - **Dataset**: Trained on a large dataset of music-text pairs, with text generated by pretrained language models.\n",
       "   - **Evaluation Metrics**: Frechet Audio Distance (FAD) and MuLan similarity score.\n",
       "\n",
       "2. **JEN-1**:\n",
       "   - **Authors**: Li et al. (2023)\n",
       "   - **Methodology**: Combines autoregressive and non-autoregressive training to directly model waveforms, avoiding fidelity loss associated with spectrogram conversion. It supports multi-task training for various music generation tasks.\n",
       "   - **Dataset**: Not explicitly detailed, but emphasizes the generation of high-fidelity music.\n",
       "   - **Evaluation Metrics**: FAD and CLAP scores, along with human qualitative assessments.\n",
       "\n",
       "3. **ERNIE-Music**:\n",
       "   - **Authors**: Zhu et al. (2023)\n",
       "   - **Methodology**: Focuses on generating music waveforms directly from free-form text using a diffusion model. It employs weak supervision techniques to address the challenge of limited text-music parallel data.\n",
       "   - **Dataset**: Created from web resources to enhance the diversity of text-music pairs.\n",
       "   - **Evaluation Metrics**: Text-music relevance and music quality scores.\n",
       "\n",
       "#### Key Findings\n",
       "\n",
       "- **Performance Metrics**:\n",
       "  - **Noise2Music** achieved strong alignment with text prompts, demonstrating that the waveform model outperformed the spectrogram model in terms of FAD and MuLan scores. The human listening tests confirmed the semantic alignment of the generated music with the text prompts.\n",
       "  - **JEN-1** reported a FAD score of 2.0 and a CLAP score of 0.33, with qualitative assessments yielding scores of 85.7/100 for text-to-music quality and 82.8/100 for alignment. This indicates a high level of fidelity and alignment with text prompts.\n",
       "  - **ERNIE-Music** outperformed existing methods with text-music relevance and music quality scores of 2.43 and 3.63, respectively. The study highlighted that free-form text conditioning significantly enhanced text-music relevance compared to predefined tags.\n",
       "\n",
       "- **Model Comparisons**:\n",
       "  - **Intermediate Representations**: Noise2Music explored both spectrograms and waveforms, finding that the waveform model provided better interpretability and performance. In contrast, JEN-1 and ERNIE-Music focused on direct waveform generation, which eliminated fidelity loss and improved overall quality.\n",
       "  - **Text Conditioning**: All three models utilized text conditioning, but ERNIE-Music's approach of using free-form text was particularly effective in enhancing relevance. Noise2Music and JEN-1 relied on structured text prompts, which may limit flexibility but still yielded high-quality results.\n",
       "\n",
       "- **Human Evaluation**: Both JEN-1 and ERNIE-Music included human qualitative assessments, which are crucial for understanding the subjective quality of generated music. JEN-1's scores indicate a strong preference for its output, while ERNIE-Music's results suggest that free-form text can lead to more relevant music generation.\n",
       "\n",
       "#### Limitations and Future Directions\n",
       "\n",
       "- **Noise2Music** noted the need for improvements in model interpretability and efficiency, suggesting that while the spectrogram approach is scalable, the waveform model's interpretability is advantageous.\n",
       "- **JEN-1** highlighted the potential for further validation across diverse datasets to ensure robustness and generalizability, particularly in real-world applications.\n",
       "- **ERNIE-Music** acknowledged limitations such as fixed-length outputs and slow generation speeds, proposing future work to optimize these aspects and expand the dataset to include vocal music.\n",
       "\n",
       "### Conclusion\n",
       "\n",
       "The results from these studies collectively underscore the potential of diffusion models for text-conditioned music generation. Each model presents unique strengths, with JEN-1 and ERNIE-Music leading in terms of waveform generation and text-music alignment. Future research should focus on enhancing model efficiency, interpretability, and the diversity of training datasets to further advance the field of generative music models.\n",
       "\n",
       "### Conclusions\n",
       "\n",
       "The systematic review of recent advancements in text-conditioned music generation using diffusion models highlights significant progress in the field, particularly through the works of Noise2Music, JEN-1, and ERNIE-Music. Each of these studies presents innovative methodologies that leverage the capabilities of diffusion models to generate high-fidelity music from textual descriptions, addressing various challenges associated with music generation.\n",
       "\n",
       "**Key Findings:**\n",
       "1. **Effectiveness of Diffusion Models**: All three studies demonstrate the efficacy of diffusion models in generating music that aligns closely with text prompts. Noise2Music and JEN-1 both emphasize the importance of directly modeling waveforms, which mitigates fidelity loss associated with spectrogram conversions. This approach is further validated by ERNIE-Music, which showcases the ability to generate music waveforms directly from free-form text, enhancing text-music relevance.\n",
       "\n",
       "2. **Intermediate Representations**: Noise2Music explores two types of intermediate representations—spectrograms and lower-fidelity audio—while JEN-1 and ERNIE-Music focus on direct waveform generation. The findings suggest that while spectrograms may offer scalability, direct waveform generation provides superior interpretability and quality, as evidenced by the performance metrics reported in these studies.\n",
       "\n",
       "3. **Text Conditioning**: The studies highlight the significance of text conditioning formats. ERNIE-Music's findings indicate that free-form text conditioning yields better results in text-music relevance compared to predefined tags. This suggests a need for flexibility in how text prompts are structured to maximize the effectiveness of the generation process.\n",
       "\n",
       "4. **Model Performance**: JEN-1 stands out for its computational efficiency and superior performance in both text-music alignment and overall music quality, achieving high scores in human evaluations. Noise2Music and ERNIE-Music also report strong performance metrics, reinforcing the potential of diffusion models in this domain.\n",
       "\n",
       "5. **Challenges and Limitations**: Despite the advancements, several challenges remain. The reliance on large datasets, particularly those sourced from the web, raises concerns about biases and inconsistencies in training data. Additionally, issues such as slow generation speeds, fixed lengths of generated music, and the absence of vocal music in some models highlight areas for further improvement.\n",
       "\n",
       "**Future Directions:**\n",
       "The future of research in text-conditioned music generation using diffusion models appears promising. Key areas for exploration include:\n",
       "- **Improving Model Interpretability**: Enhancing the interpretability of generated outputs could facilitate better user control and understanding of the generation process.\n",
       "- **Expanding Dataset Diversity**: Developing more comprehensive datasets that include a wider variety of music styles and languages could improve the generalizability of the models.\n",
       "- **Optimizing Generation Speed**: Addressing the slow generation speeds observed in some models will be crucial for practical applications, particularly in real-time music generation scenarios.\n",
       "- **Incorporating External Knowledge**: Future models could benefit from integrating external knowledge sources to enhance controllability and contextual relevance in music generation.\n",
       "\n",
       "In conclusion, the advancements in diffusion models for music generation from text prompts represent a significant leap forward in the field of generative music technology. As researchers continue to refine these models and address existing challenges, the potential for creating diverse, high-quality music that resonates with user intent will only grow, paving the way for innovative applications in both artistic and commercial contexts.\n",
       "\n",
       "### References\n",
       "\n",
       "Huang, Q., Park, D. S., Wang, T., Denk, T. I., Ly, A., Chen, N., Zhang, Z., Zhang, Z., Yu, J., Frank, C., Engel, J., Le, Q. V., Chan, W., Chen, Z., & Han, W. (2023). *Noise2Music: Text-conditioned music generation with diffusion models*. Retrieved from https://google-research.github.io/noise2music\n",
       "\n",
       "Li, P. P., Chen, B., Yao, Y., Wang, Y., Wang, A., & Wang, A. (2023). *JEN-1: Text-guided universal music generation with omnidirectional diffusion models*. Retrieved from https://www.futureverse.com/research/jen/demos/jen1\n",
       "\n",
       "Zhu, P., Pang, C., Chai, Y., Li, L., Wang, S., Sun, Y., Tian, H., & Wu, H. (2023). *ERNIE-Music: Text-to-waveform music generation with diffusion models*. Retrieved from https://reurl.cc/94W4yO\n",
       "\n",
       "### Note:\n",
       "- Ensure that the URLs are accessible and lead to the correct documents.\n",
       "- If any additional references are used in the systematic review, they should be added to this list following the same format."
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display, Markdown\n",
    "final_paper=result['draft'][-1].content\n",
    "display(Markdown(final_paper))"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [
    "vsujzwHTBtPN",
    "_b92uleGIv4s",
    "zuZhH9xFB2mO",
    "rt0b1YgrA5kt",
    "KvAL9WpGKwn4",
    "OSzhzD2XLLjF",
    "zyjVdzjsLaaa",
    "GVo4-ZX3MVdU",
    "2mm4sV0gMU_B",
    "PEa7c69xNwvT",
    "U0h7B4l1OIE7",
    "kpMd0PaqOaKw",
    "ywWFbXpGPK4d",
    "fwKaERxoPom1"
   ],
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
